from fastapi import FastAPI, Request, Response, Depends, HTTPException, UploadFile, File
from fastapi.responses import FileResponse
from fastapi.middleware.cors import CORSMiddleware
from typing import List
import json
from datetime import datetime
import logging
import os
from pathlib import Path

from app.utils.redis import RedisStore
from app.utils.auth import get_auth, verify_password
from app.utils.device import get_device_id

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = FastAPI(
    title="剪贴板同步 API",
    description="跨设备剪贴板同步服务的 API 文档",
    version="1.0.0",
    docs_url="/docs/api",
    redoc_url=None
)

# 配置 CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000", "https://clipboard.mlover.site"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 创建上传目录
UPLOAD_DIR = Path("public/downloads")
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)

@app.post("/api/clipboard/sync")
async def sync_clipboard(request: Request, _: bool = Depends(get_auth)):
    """同步剪贴板内容"""
    try:
        form = await request.form()
        content = form.get("content", "").strip()
        if not content:
            raise HTTPException(status_code=400, detail="Content cannot be empty")
            
        device_id = get_device_id(request)
        logger.info(f"Syncing clipboard from device: {device_id}")
        logger.info(f"Content: {content}")
        
        try:
            result = RedisStore.add_clipboard(content, device_id)
            logger.info(f"Successfully synced clipboard: {result}")
            return result
        except Exception as e:
            logger.error(f"Error saving to Redis: {e}")
            raise HTTPException(status_code=500, detail="Error saving content")
            
    except Exception as e:
        logger.error(f"Unexpected error in sync: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@app.get("/api/clipboard/history")
async def get_history(page: int = 1, page_size: int = 20, _: bool = Depends(get_auth)) -> dict:
    """获取剪贴板历史记录，支持分页
    
    Args:
        page: 页码，从1开始
        page_size: 每页记录数，默认20条
    """
    try:
        logger.info(f"Fetching clipboard history page {page}, size {page_size}")
        history = RedisStore.get_clipboard_history(page, page_size)
        logger.info(f"Found {len(history['items'])} items in current page")
        return history
    except Exception as e:
        logger.error(f"Error fetching history: {e}")
        raise HTTPException(status_code=500, detail="Error fetching history")

@app.delete("/api/clipboard/delete/{item_id}")
async def delete_item(item_id: str, _: bool = Depends(get_auth)):
    """删除指定的剪贴板记录"""
    try:
        logger.info(f"Deleting clipboard item: {item_id}")
        success = RedisStore.delete_clipboard(item_id)
        if not success:
            raise HTTPException(status_code=404, detail="Item not found")
        return {"success": True}
    except Exception as e:
        logger.error(f"Error deleting item: {e}")
        raise HTTPException(status_code=500, detail="Error deleting item")

@app.delete("/api/clipboard/clear")
async def clear_history(_: bool = Depends(get_auth)):
    """清除所有历史记录"""
    try:
        logger.info("Clearing all clipboard history")
        RedisStore.clear_all_history()
        return {"success": True}
    except Exception as e:
        logger.error(f"Error clearing history: {e}")
        raise HTTPException(status_code=500, detail="Error clearing history")

@app.get("/")
async def health_check():
    """健康检查"""
    try:
        # 测试 Redis 连接
        RedisStore.get_latest()
        return {
            "status": "ok",
            "timestamp": datetime.now().isoformat(),
            "redis": "connected"
        }
    except Exception as e:
        logger.error(f"Health check failed: {e}")
        return {
            "status": "error",
            "timestamp": datetime.now().isoformat(),
            "error": str(e)
        }

@app.post("/api/login")
async def login(request: Request, response: Response):
    """登录接口"""
    try:
        # 支持 JSON 和表单数据
        content_type = request.headers.get("content-type", "").lower()
        if "application/json" in content_type:
            data = await request.json()
            password = data.get("password")
            remember = data.get("remember", False)
        else:
            form = await request.form()
            password = form.get("password")
            remember = form.get("remember", "false").lower() == "true"
        
        if not password:
            raise HTTPException(status_code=401, detail="Password is required")
            
        if not verify_password(password):
            raise HTTPException(status_code=401, detail="Invalid password")
            
        # 设置 cookie，如果选择记住用户则设置 30 天过期时间
        max_age = 30 * 24 * 60 * 60 if remember else None  # 30 天或会话结束
        response.set_cookie(
            key="auth",
            value=password,
            httponly=True,
            secure=True,
            samesite="lax",
            max_age=max_age
        )
        return {"success": True}
    except HTTPException as e:
        raise e
    except Exception as e:
        logger.error(f"Login error: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@app.post("/api/upload/android")
async def upload_android_apk(
    file: UploadFile = File(...),
    _: bool = Depends(get_auth)
):
    """上传 Android APK 文件"""
    try:
        if not file.filename.endswith('.apk'):
            raise HTTPException(status_code=400, detail="只能上传 APK 文件")
        
        # 保存文件
        file_path = UPLOAD_DIR / "clipboard-sync.apk"
        with open(file_path, "wb") as f:
            content = await file.read()
            f.write(content)
            
        return {"success": True, "filename": file.filename}
    except Exception as e:
        logger.error(f"Error uploading APK: {e}")
        raise HTTPException(status_code=500, detail="上传失败")

@app.post("/api/upload/windows")
async def upload_windows_setup(
    file: UploadFile = File(...),
    _: bool = Depends(get_auth)
):
    """上传 Windows 安装包"""
    try:
        if not file.filename.endswith('.exe'):
            raise HTTPException(status_code=400, detail="只能上传 EXE 文件")
        
        # 保存文件
        file_path = UPLOAD_DIR / "clipboard-sync-setup.exe"
        with open(file_path, "wb") as f:
            content = await file.read()
            f.write(content)
            
        return {"success": True, "filename": file.filename}
    except Exception as e:
        logger.error(f"Error uploading Windows setup: {e}")
        raise HTTPException(status_code=500, detail="上传失败")

@app.get("/api/download/android")
async def download_android_apk(_: bool = Depends(get_auth)):
    """下载 Android APK 文件"""
    try:
        file_path = UPLOAD_DIR / "clipboard-sync.apk"
        if not file_path.exists():
            raise HTTPException(status_code=404, detail="APK 文件不存在")
            
        return FileResponse(
            file_path,
            media_type="application/vnd.android.package-archive",
            filename="clipboard-sync.apk"
        )
    except Exception as e:
        logger.error(f"Error downloading APK: {e}")
        raise HTTPException(status_code=500, detail="下载失败")

@app.get("/api/download/windows")
async def download_windows_setup(_: bool = Depends(get_auth)):
    """下载 Windows 安装包"""
    try:
        file_path = UPLOAD_DIR / "clipboard-sync-setup.exe"
        if not file_path.exists():
            raise HTTPException(status_code=404, detail="安装包不存在")
            
        return FileResponse(
            file_path,
            media_type="application/x-msdownload",
            filename="clipboard-sync-setup.exe"
        )
    except Exception as e:
        logger.error(f"Error downloading Windows setup: {e}")
        raise HTTPException(status_code=500, detail="下载失败")

@app.get("/api/versions")
async def get_versions():
    """获取软件版本信息"""
    try:
        android_path = UPLOAD_DIR / "clipboard-sync.apk"
        windows_path = UPLOAD_DIR / "clipboard-sync-setup.exe"
        
        return {
            "android": {
                "exists": android_path.exists(),
                "size": android_path.stat().st_size if android_path.exists() else 0,
                "last_modified": datetime.fromtimestamp(android_path.stat().st_mtime).isoformat() if android_path.exists() else None
            },
            "windows": {
                "exists": windows_path.exists(),
                "size": windows_path.stat().st_size if windows_path.exists() else 0,
                "last_modified": datetime.fromtimestamp(windows_path.stat().st_mtime).isoformat() if windows_path.exists() else None
            }
        }
    except Exception as e:
        logger.error(f"Error getting versions: {e}")
        raise HTTPException(status_code=500, detail="获取版本信息失败")
